package org.madmatrix.glib.log;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.madmatrix.glib.util.FileUtil;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

/**
 * 日志输出到文件
 * 注意：如果当前设备没有SD卡，日志无法保存
 * 
 * @author madmatrix
 */
final class FileLogger implements ILogger {

	private String tag;

	private static String fileLogDir;

	private static FileLogServices logServices;

	private SimpleDateFormat logFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());

	public FileLogger(final String tag, String fileLogDir) {
		this.tag = tag;
		FileLogger.fileLogDir = fileLogDir;

		if (logServices == null) {
			logServices = new FileLogServices();
		}
	}

	public void d(final String msg) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg);
		}
	}

	public void d(final String msg, final Throwable t) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg, t);
		}
	}

	public void i(final String msg) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg);
		}
	}

	public void i(final String msg, final Throwable t) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg, t);
		}
	}

	public void w(final String msg) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg);
		}
	}

	public void w(final String msg, final Throwable t) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg, t);
		}
	}

	public void e(final String msg) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg);
		}
	}

	public void e(final String msg, final Throwable t) {
		if (LoggerFactory.enableLogger && logServices != null) {
			logServices.log(tag, getLogPrefix() + msg, t);
		}
	}

	/**
	 * 启动独立的线程负责日志写操作，对于log日志并不是实时写文件，而是日志积累到一定大小时合并写
	 */
	private static class FileLogServices {

		private FileHandler mHandler;

		private Looper mServiceLooper;

		private StringBuffer cacheLog = new StringBuffer();

		/** 日志分段大小. */
		private static final long MAX_CACHE_LOG_SIZE = 100 * 1024;

		/** 每个日志文件大小. */
		private static final long MAX_LOG_fILE_SIZE = 2 * 1024 * 1024;

		private static final int MESSAGE_WRITELOG = 100;

		private static final int MESSAGE_QUIT = 101;
		
		public FileLogServices() {
			HandlerThread thread = new HandlerThread("GLib_FileLogger_Service", android.os.Process.THREAD_PRIORITY_BACKGROUND);
			thread.start();

			mServiceLooper = thread.getLooper();
			mHandler = new FileHandler(mServiceLooper);
		}
		
		public void log(String tag, String msg) {
			sendLog(msg, true);
		}

		public void log(String tag, String msg, Throwable t) {
			sendLog(msg + "\n" + Log.getStackTraceString(t), true);
		}

		public void stop() {
			// 一次性写入未及时写入的数据
			sendLog(cacheLog.toString(), false);

			// 退出守护进程
			mHandler.sendEmptyMessage(MESSAGE_QUIT);
		}

		/**
		 * sendLog
		 * 
		 * @param log
		 * @param shouldCheckSize
		 *            写入时是否需要检查缓存日志的大小，退出时不需要检查
		 */
		private void sendLog(String log, boolean shouldCheckSize) {

			if (TextUtils.isEmpty(log)) {
				return;
			}

			cacheLog.append("\n" + log);

			if (!shouldCheckSize || cacheLog.length() > MAX_CACHE_LOG_SIZE) {
				Message message = mHandler.obtainMessage(MESSAGE_WRITELOG);
				message.obj = cacheLog.toString();
				message.sendToTarget();

				cacheLog.delete(0, cacheLog.length());
			}
		}
		
		private class FileHandler extends Handler {

			public FileHandler(Looper looper) {
				super(looper);
			}

			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);

				switch (msg.what) {
				case MESSAGE_WRITELOG:
					if (msg.obj != null) {
						writeLog(msg.obj.toString());
					}

					break;
				case MESSAGE_QUIT:
					if (mServiceLooper != null) {
						mServiceLooper.quit();
					}

					break;
				default:
					break;
				}
			}

			/**
			 * 输出日志到文件
			 * 
			 * @param log
			 */
			private void writeLog(String log) {
				File logFile = getCurLogFile();
				if (logFile == null) {
					return;
				}

				FileOutputStream os = null;
				try {
					os = new FileOutputStream(logFile, true);
					byte[] data = log.getBytes();
					os.write(data, 0, data.length);
				} catch (Exception e) {
				} finally {
					if (os != null) {
						try {
							os.close();
						} catch (Exception e2) {
						}
					}
				}
			}

			/**
			 * 返回满足条件的日志文件
			 * 注意：
			 * 1. 如果当前设备没有SD卡，日志无法保存
			 * 2. 日志文件名的格式为：{DIR}/yyyyMMddHHmmssSSS.log
			 * 
			 * @return 如果没有找到则返回null
			 */
			private File getCurLogFile() {
				// 检查并尝试创建存放日志文件的路径
				if (TextUtils.isEmpty(fileLogDir)) {
					fileLogDir = FileUtil.getSdcardRoot();
					if (TextUtils.isEmpty(fileLogDir)) { // 没有sdcard，只能返回null
						return null;
					}
				}
				
				FileUtil.checkAndMakeDir(fileLogDir);
				
				// 先检查当前日志目录下是否有日志文件
				File logDir = new File(fileLogDir);
				File[] fileList = logDir.listFiles();
				if (fileList.length != 0) {
					// 找出最近更新的文件
					int latestInd = 0;
					for (int i = 1; i < fileList.length; ++i) {
						if (fileList[i].lastModified() > fileList[latestInd].lastModified()) {
							latestInd = i;
						}
					}
					
					// 如果该文件大小没有达到上限，则返回该文件
					if (fileList[latestInd].length() < MAX_LOG_fILE_SIZE) {
						return fileList[latestInd];
					}
				}
				
				// 日志目录为空，或者目录下所有文件大小已达最大值，则创建新文件
				SimpleDateFormat formeter = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault());
				return new File(fileLogDir + File.separator + formeter.format(new Date()) + ".log");
			}
		}
	}

	@Override
	public void destory() {
		if (logServices != null) {
			logServices.stop();
			logServices = null;
		}
	}

	@Override
	public String getLogPrefix() {
		return logFormatter.format(new Date());
	}
}
